/*+*********************************************************
Filename: ety.v
Description:
  transfer udp data on ethernet.

Modification:
2024.03.14 creation by H.Zheng, combine modules from 3rd party open source
           with FIFO module.
2024.04.16 change ip&mac addr as parameter
**********************************************************-*/


module ety#(
	parameter DEST_MAC_ADDR = 48'hff_ff_ff_ff_ff_ff, 
	parameter SRC_MAC_ADDR = {8'h06, 8'h00, 8'haa, 8'hbb, 8'h0c, 8'hdd}, 
	parameter DEST_IP_ADDR = {8'd192,8'd168,8'd15,8'd15}, 
	parameter SRC_IP_ADDR = {8'd192,8'd168,8'd15,8'd14}, 
	parameter DEST_UDP_PORT = 16'd5678, 
	parameter SRC_UDP_PORT = 16'd1234
)(
  input wire clk1m,
  input wire rst_n,
  output wire ety_phyrst,
  input wire netrmii_clk50m,
  input wire netrmii_rx_crs,
  output  wire netrmii_mdc,
  output wire netrmii_txen,
  inout wire netrmii_mdio,
  output wire [1:0] netrmii_txd,
  input wire [1:0] netrmii_rxd,

  input wire [31:0] data,
  input wire data_clk
);


/**
 * FIFO section
 */
  wire fifo_afull_flag;
  wire [31:0] fifo_out_data;
  wire fifo_rd_clk;
  wire fifo_rd_en;

	FIFO_HS_Top u_fifo(   //64 deep * 32 byte, 48 trigger almost full
		.Data(data), //input [31:0] Data
		.WrClk(data_clk), //input WrClk
		.RdClk(fifo_rd_clk), //input RdClk
		.WrEn(1'b1), //input WrEn
		.RdEn(fifo_rd_en), //input RdEn
		.Almost_Full(fifo_afull_flag), //output Almost_Full
		.Q(fifo_out_data), //output [31:0] Q
		.Empty(), //output Empty
		.Full() //output Full
	);


/**
 * UDP section
 */

  wire udp_tx_start_en = fifo_afull_flag;
  wire [31:0] udp_tx_data = fifo_out_data;
  wire [15:0] udp_tx_byte_num = 16'd192; //48*4


  wire            tx_done;            //表明传输完成的信号
  reg   [7:0]     eth_tx_status;      //发送状态

  wire            rmii_tx_en;         //rmii传输使能信号
  wire  [7:0]     rmii_tx_data;       //需要传输的rmii数据

  wire udp_tx_req;
  assign fifo_rd_clk = netrmii_clk50m;
  assign fifo_rd_en = udp_tx_req;

  udp_tx#(.DES_MAC(DEST_MAC_ADDR), .BOARD_MAC(SRC_MAC_ADDR), 
          .DES_IP(DEST_IP_ADDR), .BOARD_IP(SRC_IP_ADDR),
          .DEST_UDP_PORT(DEST_UDP_PORT), .SRC_UDP_PORT(SRC_UDP_PORT)) u_udp(
    .clk                  (netrmii_clk50m     ),           //发送时钟：50M
    .rst_n                (rst_n              ),           //复位
    .tx_start_en          (udp_tx_start_en    ),           //以太网开始发送信号
    .tx_data              (udp_tx_data        ),           //需要发送的数据（32bit）
    .tx_byte_num          (udp_tx_byte_num    ),           //以太网发送的有效字节数 单位:byte
    .tx_done              (tx_done        ),           //以太网发送完成信号
    .tx_req               (udp_tx_req         ),           //读数据请求信号
    .rmii_tx_en           (rmii_tx_en         ),           //rmii发送有效
    .rmii_tx_data         (rmii_tx_data       )            //rmii发送数据
  );


/**
 * ETY init section
 */
  reg rphyrst;
  reg phy_rdy;
  reg SMI_trg;
  wire SMI_ack;
  wire SMI_ready;
  reg SMI_rw;
  reg [4:0] SMI_adr;
  wire [15:0] SMI_data;
  reg [15:0] SMI_wdata;
  reg[7:0] SMI_status;

  always@(posedge clk1m or negedge rst_n)begin
    if(rst_n == 1'b0)begin
      phy_rdy <= 1'b0;
      rphyrst <= 1'b0;
      SMI_trg <= 1'b0;
      SMI_adr <= 5'd1;
      SMI_rw <= 1'b1;
      SMI_status <= 0;
    end else begin
      rphyrst <= 1'b1;
      if(phy_rdy == 1'b0)begin
        SMI_trg <= 1'b1;
        if(SMI_ack && SMI_ready)begin
          case(SMI_status)
            0:begin
              SMI_adr <= 5'd31;
              SMI_wdata <= 16'h7;
              SMI_rw <= 1'b0;

              SMI_status <= 1;
            end
            1:begin
              SMI_adr <= 5'd16;
              SMI_wdata <= 16'h0FFE;  //16'h7FFE not work, no clk50M

              SMI_status <= 2;
            end
            2:begin
              SMI_rw <= 1'b1;

              SMI_status <= 3;
            end
            3:begin
              SMI_adr <= 5'd31;
              SMI_wdata <= 16'h0;
              SMI_rw <= 1'b0;

              SMI_status <= 4;
            end
            4:begin
              SMI_adr <= 5'd1;
              SMI_rw <= 1'b1;

              SMI_status <= 5;
            end
            5:begin
              if(SMI_data[2])begin
                phy_rdy <= 1'b1;
                SMI_trg <= 1'b0;
              end
            end
          endcase
        end
      end
    end
  end

  SMI_ct ct(
    .clk(clk1m),
    .rst(rphyrst),
    .rw(SMI_rw),
    .trg(SMI_trg),
    .ready(SMI_ready),
    .ack(SMI_ack),
    .phy_adr(5'd1),
    .reg_adr(SMI_adr),
    .data(SMI_wdata),
    .smi_data(SMI_data),
    .mdio(netrmii_mdio)
  );
  assign netrmii_mdc = clk1m;
  assign ety_phyrst = rphyrst;



/**
 * ETH tx section
 */

  reg eth_tx_rstn;

  reg [7:0] eth_tx_data;
  reg eth_tx_en;
  wire tx_bz;
  wire tx_av;

  tx_ct eth_tx(
    .clk(netrmii_clk50m), 
    .rst(phy_rdy),
    .data(eth_tx_data),
    .tx_en(eth_tx_en),
    .tx_bz(tx_bz),
    .tx_av(tx_av),
    .p_txd(netrmii_txd),
    .p_txen(netrmii_txen)
  );

  always@(posedge netrmii_clk50m or negedge rst_n)begin
    if(!rst_n) begin  
      eth_tx_status <= 0;
    end else begin
      case(eth_tx_status)
        0:begin                    //wait for phy ready and tx_trigger
          eth_tx_en <= 0;
          if(phy_rdy == 1) begin
            eth_tx_status <= 1;
          end
        end
        1:begin                    //wait for tx_bz==0  //表示线路空闲
          if(tx_bz == 1'b0) begin
            eth_tx_status <= 2;
          end
        end
        2:begin
          if(rmii_tx_en == 1) begin
            eth_tx_en <= 1;
            eth_tx_data <= rmii_tx_data;
            eth_tx_status <= 3;
          end
        end
        3:begin
          eth_tx_data <= rmii_tx_data;
          if(tx_done) begin      //结束条件
            eth_tx_status <= 4;
          end
        end
        4:begin
          eth_tx_en <= 0;
          eth_tx_status <= 0;
        end
      endcase
    end
  end


endmodule 